/**
 * Copyright &copy; 2012-2016 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
 */
package com.thinkgem.jeesite.modules.sys.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.thinkgem.jeesite.common.config.Global;
import com.thinkgem.jeesite.common.persistence.BaseConstants;
import com.thinkgem.jeesite.common.utils.Exceptions;
import com.thinkgem.jeesite.common.utils.SpringContextHolder;
import com.thinkgem.jeesite.common.utils.StringUtils;
import com.thinkgem.jeesite.modules.sys.entity.SysLog;
import com.thinkgem.jeesite.modules.sys.entity.SysMenu;
import com.thinkgem.jeesite.modules.sys.entity.SysUser;
import com.thinkgem.jeesite.modules.sys.service.SysLogService;
import com.thinkgem.jeesite.modules.sys.service.SysMenuService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * 日志工具类
 *
 * @author kongxiangsheng
 * @version 2014-11-7
 */
public class LogUtils {
    private static SysLogService sysLogService = SpringContextHolder.getBean(SysLogService.class);
    private static SysMenuService sysMenuService = SpringContextHolder.getBean(SysMenuService.class);

    /**
     * 保存日志
     */
    public static void saveLog(HttpServletRequest request, String title) {
        saveLog(request, null, null, title);
    }

    /**
     * 保存日志
     */
    public static void saveLog(HttpServletRequest request, Object handler, Exception ex, String title) {
        SysUser user = UserUtils.getUser();
        if (user != null && user.getId() != null) {
            SysLog syslog = new SysLog();
            syslog.setTitle(title);
            syslog.setType(ex == null ? BaseConstants.LOG_TYPE_ACCESS : BaseConstants.LOG_TYPE_EXCEPTION);
            syslog.setRemoteAddr(StringUtils.getRemoteAddr(request));
            syslog.setUserAgent(request.getHeader("user-agent"));
            syslog.setRequestUri(request.getRequestURI());
            syslog.setParams(request.getParameterMap());
            syslog.setMethod(request.getMethod());
            // 异步保存日志
            new SaveLogThread(syslog, handler, ex).start();
        }
    }

    /**
     * 获取菜单名称路径（如：系统设置-机构用户-用户管理-编辑）
     */
    public static String getMenuNamePath(String requestUri, String permission) {
        String href = StringUtils.substringAfter(requestUri, Global.getAdminPath());
        Map<String, String> menuMap = SysCacheUtils.getMenuPathMap();
        if (menuMap == null) {
            menuMap = Maps.newHashMap();
            List<SysMenu> menuList = sysMenuService.getAllOrdered();
            for (SysMenu sysMenu : menuList) {
                // 获取菜单名称路径（如：系统设置-机构用户-用户管理-编辑）
                String namePath = "";
                if (sysMenu.getParentIds() != null) {
                    List<String> namePathList = Lists.newArrayList();
                    for (String id : StringUtils.split(sysMenu.getParentIds(), ",")) {
                        if (BaseConstants.ROOT_MENU_ID.equals(id)) {
                            continue; // 过滤根节点
                        }
                        for (SysMenu m : menuList) {
                            if (m.getId().equals(id)) {
                                namePathList.add(m.getName());
                                break;
                            }
                        }
                    }
                    namePathList.add(sysMenu.getName());
                    namePath = StringUtils.join(namePathList, "-");
                }
                // 设置菜单名称路径
                if (StringUtils.isNotBlank(sysMenu.getHref())) {
                    menuMap.put(sysMenu.getHref(), namePath);
                } else if (StringUtils.isNotBlank(sysMenu.getPermission())) {
                    for (String p : StringUtils.split(sysMenu.getPermission())) {
                        menuMap.put(p, namePath);
                    }
                }

            }
            SysCacheUtils.setMenuPathMap(menuMap);
        }
        String menuNamePath = menuMap.get(href);
        if (menuNamePath == null) {
            for (String p : StringUtils.split(permission)) {
                menuNamePath = menuMap.get(p);
                if (StringUtils.isNotBlank(menuNamePath)) {
                    break;
                }
            }
            if (menuNamePath == null) {
                return "";
            }
        }
        return menuNamePath;
    }

    /**
     * 保存日志线程
     */
    public static class SaveLogThread extends Thread {

        private SysLog syslog;
        private Object handler;
        private Exception ex;

        public SaveLogThread(SysLog syslog, Object handler, Exception ex) {
            super(SaveLogThread.class.getSimpleName());
            this.syslog = syslog;
            this.handler = handler;
            this.ex = ex;
        }

        @Override
        public void run() {
            // 获取日志标题
            if (StringUtils.isBlank(syslog.getTitle())) {
                String permission = "";
                if (handler instanceof HandlerMethod) {
                    Method m = ((HandlerMethod) handler).getMethod();
                    RequiresPermissions rp = m.getAnnotation(RequiresPermissions.class);
                    permission = (rp != null ? StringUtils.join(rp.value(), ",") : "");
                }
                syslog.setTitle(getMenuNamePath(syslog.getRequestUri(), permission));
            }
            // 如果有异常，设置异常信息
            syslog.setException(Exceptions.getStackTraceAsString(ex));
            // 如果无标题并无异常日志，则不保存信息
            if (StringUtils.isBlank(syslog.getTitle()) && StringUtils.isBlank(syslog.getException())) {
                return;
            }
            // 保存日志信息
            sysLogService.save(syslog);
        }
    }

}
